
#include "oled.h"

#include "oledfont.h"

#define Oled_WR_CMD(c)                                                   \
  {                                                                      \
    mem = (c);                                                           \
    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT, &mem, 1, \
                      0x100);                                            \
  }

#define Oled_WR_DATA(d)                                                  \
  {                                                                      \
    mem = (d);                                                           \
    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT, &mem, 1, \
                      0x100);                                            \
  }
/**
 * @variable: mem
 * @description: 缓存发送的数据
 */
uint8_t mem;
/**
 * @variable: turn
 * @description: 控制oled屏是否反向显示, 即黑变白, 白变黑. 可取0xFF或0x00
 */
uint8_t turn;

/**
 * @function: void Oled_Init(void)
 * @description: OLED初始化
 * @return {*}
 */
void Oled_Init(void) {
  // 初始化所需要的指令
  static const uint8_t CMD_Data[] = {
      0xAE, 0xD5, 0x80, 0xA8, 0x3F, 0xD3, 0x00, 0x40, 0xA1, 0xC8, 0xDA, 0x12,
      0x81, 0xCF, 0xD9, 0xF1, 0xDB, 0x40, 0xA4, 0xA6, 0x8D, 0x14, 0xAF};

  HAL_Delay(100);  // 预充电时间
  HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
                    (uint8_t *)CMD_Data, 23, 0x100);
}

/**
 * @function: void Oled_On(void)
 * @description: 更新显示
 * @return {*}
 */
void Oled_On(void) {
  uint8_t i, n;
  for (i = 0; i < 8; i++) {
    Oled_WR_CMD(0xb0 + i);  // 设置页地址（0~7）
    Oled_WR_CMD(0x00);      // 设置显示位置—列低地址
    Oled_WR_CMD(0x10);      // 设置显示位置—列高地址
    for (n = 0; n < 128; n++) Oled_WR_DATA(1);
  }
}

/**
 * @function: Oled_ClearAll(void)
 * @description: 清屏,整个屏幕是黑色的!和没点亮一样!!!
 * @return {*}
 */
void Oled_ClearAll(void) {
  uint8_t i, n;
  for (i = 0; i < 8; i++) {
    Oled_WR_CMD(0xb0 + i);  // 设置页地址（0~7）
    Oled_WR_CMD(0x00);      // 设置显示位置—列低地址
    Oled_WR_CMD(0x10);      // 设置显示位置—列高地址
    for (n = 0; n < 128; n++) Oled_WR_DATA(0);
  }
}

/**
 * @function: void Oled_Display_On(void)
 * @description: 开启OLED显示
 * @return {*}
 */
void Oled_Display_On(void) {
  Oled_WR_CMD(0X8D);  // SET DCDC命令
  Oled_WR_CMD(0X14);  // DCDC ON
  Oled_WR_CMD(0XAF);  // DISPLAY ON,打开显示
}

/**
 * @function: void Oled_Display_Off(void)
 * @description: 关闭OLED显示
 * @return {*}
 */
void Oled_Display_Off(void) {
  Oled_WR_CMD(0X8D);  // SET DCDC命令
  Oled_WR_CMD(0X10);  // DCDC OFF
  Oled_WR_CMD(0XAE);  // DISPLAY OFF，关闭显示
}

/**
 * @function: void Oled_Set_Pos(uint8_t x, uint8_t y)
 * @description: 坐标设置
 * @param {uint8_t} x,y
 * @return {*}
 */
void Oled_Set_Pos(uint8_t x, uint8_t y) {
  Oled_WR_CMD(0xb0 + y);                  // 设置页地址（0~7）
  Oled_WR_CMD(((x & 0xf0) >> 4) | 0x10);  // 设置显示位置—列高地址
  Oled_WR_CMD(x & 0x0f);                  // 设置显示位置—列低地址
}

/**
 * @function: unsigned int oled_pow(uint8_t m,uint8_t n)
 * @description: m^n函数: 求m的n次方
 * @param {uint8_t} m,n
 * @return {unsigned int} result
 */
unsigned int oled_pow(uint8_t m, uint8_t n) {
  unsigned int result = 1;
  while (n--) result *= m;
  return result;
}

void Oled_ShowCharSmall(uint8_t x, uint8_t y, uint8_t chr) {
  unsigned char c = 0;
  uint8_t i = 0;
  c = chr - ' ';  // 得到偏移后的值
  Oled_Set_Pos(x, y);
  for (i = 0; i < 6; i++) {
    Oled_WR_DATA(F6x8[c][i] ^ turn);
  }
}

void Oled_ShowCharBig(uint8_t x, uint8_t y, uint8_t chr) {
  unsigned char c = 0, i = 0;
  c = chr - ' ';  // 得到偏移后的值
  Oled_Set_Pos(x, y);
  for (i = 0; i < 8; i++) {
    Oled_WR_DATA(F8X16[c][i] ^ turn);
  }
  Oled_Set_Pos(x, y + 1);
  for (i = 0; i < 8; i++) {
    Oled_WR_DATA(F8X16[c][i + 8] ^ turn);
  }
}

void Oled_ShowStringSmall(uint8_t x, uint8_t y, const char *chr) {
  // 使用时请自行确保字符串长度不大于21
  Oled_Set_Pos(x, y);
  while (*chr) {
    for (uint16_t i = 0; i < 6; i++) {
      Oled_WR_DATA(F6x8[*chr - ' '][i] ^ turn);
    }
    chr++;
  }
}

void Oled_ShowStringBig(uint8_t x, uint8_t y, const char *chr) {
  // 使用时请自行确保字符串长度不大于16
  const char *c = chr;
  Oled_Set_Pos(x, y);
  while (*c) {
    for (uint8_t i = 0; i < 8; i++) {
      Oled_WR_DATA(F8X16[(*c - ' ')][i] ^ turn);
    }
    c++;
  }
  Oled_Set_Pos(x, y + 1);
  while (*chr) {
    for (uint8_t i = 0; i < 8; i++) {
      Oled_WR_DATA(F8X16[(*chr - ' ')][i + 8] ^ turn);
    }
    chr++;
  }
}

void Oled_ShowNumSmall(uint8_t x, uint8_t y, uint16_t num, uint8_t len) {
  uint8_t t, temp;
  uint8_t enshow = 0;  // 是否开始显示数字
  Oled_Set_Pos(x, y);
  uint8_t i;
  for (t = 0; t < len; t++) {
    temp = (num / oled_pow(10, len - t - 1)) % 10;
    if (enshow == 0 && t < (len - 1)) {
      if (temp == 0) {
        for (i = 0; i < 6; i++) {
          Oled_WR_DATA(F6x8[0][i] ^ turn);
        }
        continue;
      } else
        enshow = 1;
    }
    temp += 16;
    for (i = 0; i < 6; i++) {
      Oled_WR_DATA(F6x8[temp][i] ^ turn);
    }
  }
}

void Oled_ShowChinese(uint8_t x, uint8_t y, uint8_t idx) {
  uint8_t t = 0;
  Oled_Set_Pos(x, y);
  for (t = 0; t < 16; t++) {
    Oled_WR_DATA(Hzk[2 * idx][t] ^ turn);  // 显示汉字的上半部分
  }

  Oled_Set_Pos(x, y + 1);
  for (t = 0; t < 16; t++) {
    Oled_WR_DATA(Hzk[2 * idx + 1][t] ^ turn);  // 显示汉字的上半部分
  }
}

void Oled_DrawBMP(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
                  uint8_t *BMP) {
  uint32_t j = 0;
  uint8_t x = 0, y = 0;

  if (y1 % 8 == 0)
    y = y1 / 8;
  else
    y = y1 / 8 + 1;
  for (y = y0; y < y1; y++) {
    Oled_Set_Pos(x0, y);
    for (x = x0; x < x1; x++) {
      Oled_WR_DATA(BMP[j++] ^ turn);  // 显示图片
    }
  }
}

void Oled_IntensityControl(uint8_t intensity) {
  Oled_WR_CMD(0x81);
  Oled_WR_CMD(intensity);
}
